`timescale 1ns / 1ps
/************************************************************\
 **  Copyright (c) 2022-2023 Gonsin, Inc.
 **  All Right Reserved.
 **  Author: http://www.anlogic.com/
 **  Description: tx_client_fifo
 **  Rev 1.0
\************************************************************/

module tx_client_fifo #(
parameter FULL_DUPLEX_ONLY = 0
)
(
// MAC Interface
input rd_clk_i,
input rd_sreset_i,
input rd_enable_i,
output reg [7:0] txdata8b_o,
output reg txdata_valid_o,
input tx_ack_i,
input tx_collision_i,
input tx_retransmit_i,
output overflow_o,

// Local-link Interface
input wr_clk_i,
input wr_sreset_i,
input [7:0] wrdata8b_i,
input wr_sofN_i,
input wr_eofN_i,
input wr_src_rdyN_i,
output wr_dst_rdyN_o,
output reg [3:0] wr4b_fifostatus_o
);

// Encode rd_state_machine states
localparam IDLE_s = 4'b0000;
localparam QUEUE1_s = 4'b0001;
localparam QUEUE2_s = 4'b0010;
localparam QUEUE3_s = 4'b0011;
localparam QUEUE_ACK_s = 4'b0100;
localparam WAIT_ACK_s = 4'b0101;
localparam FRAME_s = 4'b0110;
localparam DROP_s = 4'b0111;
localparam RETRANSMIT_s = 4'b1000;

reg [3:0] rd4b_sm;
reg [3:0] rd4b_nxt_sm;

// Encode wr_state_machine states
localparam WAIT_s = 2'b00;
localparam DATA_s = 2'b01;
localparam EOF_s = 2'b10;
localparam OVFLOW_s = 2'b11;

reg [1:0] wr2b_sm;
reg [1:0] wr2b_nxt_sm;

//----------------------------------------------------------------------------
// Define Internal Signals
//----------------------------------------------------------------------------
reg [7:0] wrdata8b_bram;
reg [7:0] wrdata8b_pipe[0:1];
reg wr_sof_pipe[0:1];
reg wr_eof_pipe[0:1];
reg wr_accept_pipe[0:1];
reg wr_accept_bram;
reg [0:0] wr_eof_bram;
reg [11:0] wr12b_addr;
wire wr_addrInc_w;
wire wr_startAddr_load_w;
wire wr_addrReload_w;
reg [11:0] wr12b_startAddr;
reg wr_fifo_full;
wire wr_en_w;
wire wr_enU_w;
wire wr_enL_w;
reg wr_ovflow_dstRdy;
wire wr_dstRdy_intN_w;

wire frame_INfifo_w;
reg rd_eof;
reg rd_eof_follow;
reg rd_eof_pipe;
reg [11:0] rd12b_addr;
wire rd_addrInc_w;
wire rd_addrReload_w;
wire [7:0] rddata8b_bramU_w;
wire [7:0] rddata8b_bramL_w;
reg  [7:0] rddata8b_pipeU;
reg  [7:0] rddata8b_pipeL;
reg  [7:0] rddata8b_pipe;
wire [0:0] rd_eof_bramU_w;
wire [0:0] rd_eof_bramL_w;
wire rd_en_w;
wire rd_en_bram_w;
reg rd_bramU;
reg rd_bramU_follow;

reg rdTran_frameTog = 1'b0;
wire wrTran_frameSync_w;
reg wrTran_frameDelay = 1'b0;
reg rdreTran_frameTog = 1'b0;
wire wrRetran_frameSync_w;
reg wrRetran_frameDelay = 1'b0;

wire wr_storeFrame_w;
reg wr_transmitFrame;
reg wr_retransmitFrame;
reg [8:0] wr9b_frames;
reg wr_frame_INfifo;

reg [3:0] rd4b_16count;
wire rdTxfer_en_w;
reg [11:0] rd12b_addr_txfer;
reg rdTxfer_tog = 1'b0;
wire wrTxfer_togSync_w;
reg wrTxfer_togDelay = 1'b0;
wire wrTxfer_en_w;
reg [11:0] wr12b_rd_addr;
reg [11:0] wr12b_addr_diff;

reg rd_drop_frame;
reg rd_retransmit;
reg [11:0] rd12b_startAddr;
wire rd_startAddr_load_w;
wire rd_startAddr_reload_w;
reg [11:0] rd12b_dec_addr;
wire rd_transmitFrame_w;
wire rd_retransmitFrame_w;

reg rd_colWindow_expire;
reg rd_colWindow_pipe[0:1];
reg wr_colWindow_pipe[0:1];
wire wr_eofState_w;
reg wr_eofState;
wire wr_fifoOverflow_w;
reg [9:0] rd10b_slotTimer;
reg wr_colWindow_expire;
wire rd_idleState_w;

//----------------------------------------------------------------------------
// Begin FIFO architecture
//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// Write State machine and control
//----------------------------------------------------------------------------

// Write state machine
// states are WAIT, DATA, EOF, OVFLOW
// clock through next state of sm
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr2b_sm <= WAIT_s;
  end
  else begin
    wr2b_sm <= wr2b_nxt_sm;
  end
end

// decode next state, combinitorial
// should never be able to overflow_o whilst not in the data state.
always@(wr2b_sm or wr_sof_pipe[1] or wr_eof_pipe[0] or wr_eof_pipe[1]
        or wr_eof_bram[0] or wr_fifoOverflow_w) begin
  case(wr2b_sm)
    WAIT_s : begin
      if(wr_sof_pipe[1] == 1'b1) begin
        wr2b_nxt_sm <= DATA_s;
      end
      else begin
        wr2b_nxt_sm <= WAIT_s;
      end
    end

   DATA_s : begin
     // wait for the end of frame to be detected
     if(wr_fifoOverflow_w == 1'b1 && wr_eof_pipe[0] == 1'b0
         && wr_eof_pipe[1] == 1'b0) begin
       wr2b_nxt_sm <= OVFLOW_s;
     end
     else if(wr_eof_pipe[1] == 1'b1) begin
       wr2b_nxt_sm <= EOF_s;
     end
     else begin
       wr2b_nxt_sm <= DATA_s;
     end
   end
   EOF_s : begin
     // if the start of frame is already in the pipe, a back to back frame
     // transmission has occured.  move straight back to frame state
     if(wr_sof_pipe[1] == 1'b1) begin
       wr2b_nxt_sm <= DATA_s;
     end
     else if(wr_eof_bram[0] == 1'b1) begin
       wr2b_nxt_sm <= WAIT_s;
     end
     else begin
       wr2b_nxt_sm <= EOF_s;
     end
   end

   OVFLOW_s : begin
     // wait until the end of frame is reached before clearing the overflow_o
     if(wr_eof_bram[0] == 1'b1) begin
       wr2b_nxt_sm <= WAIT_s;
     end
     else begin
       wr2b_nxt_sm <= OVFLOW_s;
     end
   end

   default : begin
     wr2b_nxt_sm <= WAIT_s;
   end
  endcase
end

// decode output signals.
assign wr_en_w = (wr2b_sm == OVFLOW_s) ? 1'b0 : wr_accept_bram;
assign wr_enL_w = wr_en_w & !wr12b_addr[11];
assign wr_enU_w = wr_en_w & wr12b_addr[11];

assign wr_addrInc_w = wr_en_w;

assign wr_addrReload_w = (wr2b_sm == OVFLOW_s) ? 1'b1 : 1'b0;
assign wr_startAddr_load_w = (wr2b_sm == EOF_s && wr2b_nxt_sm == WAIT_s)
                           ? 1'b1 :
                           (wr2b_sm == EOF_s && wr2b_nxt_sm == DATA_s)
                           ? 1'b1 : 1'b0;

// pause the local link flow when the fifo is full.
assign wr_dstRdy_intN_w = (wr2b_sm == OVFLOW_s) ?
                           wr_ovflow_dstRdy : wr_fifo_full;

assign wr_dst_rdyN_o = wr_dstRdy_intN_w;

// when in overflow_o and have captured ovflow eof send dst rdy high again.
assign overflow_o = (wr2b_sm == OVFLOW_s) ? 1'b1 : 1'b0;

// when in overflow_o and have captured ovflow eof send dst rdy high again.
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr_ovflow_dstRdy <= 1'b0;
  end
  else begin
    if(wr_fifoOverflow_w == 1'b1 && wr2b_sm == DATA_s) begin
      wr_ovflow_dstRdy <= 1'b0;
    end
    else if(wr_eofN_i == 1'b0 && wr_src_rdyN_i == 1'b0) begin
      wr_ovflow_dstRdy <= 1'b1;
    end
  end
end

// eof signals for use in overflow_o logic
assign wr_eofState_w = (wr2b_sm == EOF_s) ? 1'b1 : 1'b0;

always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr_eofState <= 1'b0;
  end
  else begin
    wr_eofState <= wr_eofState_w;
  end
end

//----------------------------------------------------------------------------
// Read state machine and control
//----------------------------------------------------------------------------

// clock through the read state machine
always@(posedge rd_clk_i) begin
  if(rd_sreset_i == 1'b1) begin
    rd4b_sm <= IDLE_s;
  end
  else if(rd_enable_i == 1'b1) begin
    rd4b_sm <= rd4b_nxt_sm;
  end
end

//----------------------------------------------------------------------------
// Full Duplex Only State Machine
generate if(FULL_DUPLEX_ONLY == 1) begin : gen_fd_sm
  // decode the next state
  always@(rd4b_sm or frame_INfifo_w or rd_eof or tx_ack_i) begin
    case(rd4b_sm)
      IDLE_s : begin
        // if there is a frame in the fifo start to queue the new frame
        // to the output
        if(frame_INfifo_w == 1'b1) begin
          rd4b_nxt_sm <= QUEUE1_s;
        end
        else begin
          rd4b_nxt_sm <= IDLE_s;
        end
      end

      QUEUE1_s : begin
        rd4b_nxt_sm <= QUEUE2_s;
      end

      QUEUE2_s : begin
        rd4b_nxt_sm <= QUEUE3_s;
      end

      QUEUE3_s : begin
        rd4b_nxt_sm <= QUEUE_ACK_s;
      end

      QUEUE_ACK_s : begin
        rd4b_nxt_sm <= WAIT_ACK_s;
      end

      WAIT_ACK_s : begin
        // the output pipe line is fully loaded, so wait for ack from mac
        // before moving on
        if(tx_ack_i == 1'b1) begin
          rd4b_nxt_sm <= FRAME_s;
        end
        else begin
          rd4b_nxt_sm <= WAIT_ACK_s;
        end
      end

      FRAME_s : begin
        // when the end of frame has been reached wait another frame in
        // the fifo
        if(rd_eof == 1'b1) begin
          rd4b_nxt_sm <= IDLE_s;
        end
        else begin
          rd4b_nxt_sm <= FRAME_s;
        end
      end

      default : begin
        rd4b_nxt_sm <= IDLE_s;
      end
    endcase
  end       
end   // gen_fd_sm   // full duplex state machine
endgenerate

//----------------------------------------------------------------------------
// Full and Half Duplex State Machine
generate if(FULL_DUPLEX_ONLY != 1) begin : gen_hd_sm
  // decode the next state
  // should never receive a rd_drop_frame pulse outside of the Frame state
  always @(rd4b_sm or frame_INfifo_w or rd_eof_follow or tx_ack_i or rd_drop_frame
           or rd_retransmit) begin
    case(rd4b_sm)
      IDLE_s : begin
        // if a retransmit request is detected go to retransmit state
        if(rd_retransmit == 1'b1) begin
          rd4b_nxt_sm <= RETRANSMIT_s;
        end
        // if there is a frame in the fifo then queue the new frame to
        // the output
        else if(frame_INfifo_w == 1'b1) begin
          rd4b_nxt_sm <= QUEUE1_s;
        end
        else begin
          rd4b_nxt_sm <= IDLE_s;
        end
      end

      QUEUE1_s : begin
        if(rd_retransmit == 1'b1) begin
          rd4b_nxt_sm <= RETRANSMIT_s;
        end
        else begin
          rd4b_nxt_sm <= QUEUE2_s;
        end
      end

      QUEUE2_s : begin
        if(rd_retransmit == 1'b1) begin
          rd4b_nxt_sm <= RETRANSMIT_s;
        end
        else begin
          rd4b_nxt_sm <= QUEUE3_s;
        end
      end

      QUEUE3_s : begin
        if(rd_retransmit == 1'b1) begin
          rd4b_nxt_sm <= RETRANSMIT_s;
        end
        else begin
          rd4b_nxt_sm <= QUEUE_ACK_s;
        end
      end

      QUEUE_ACK_s : begin
        if(rd_retransmit == 1'b1) begin
          rd4b_nxt_sm <= RETRANSMIT_s;
        end
        else begin
          rd4b_nxt_sm <= WAIT_ACK_s;
        end
      end

      WAIT_ACK_s : begin
        // the output pipeline is now fully loaded so wait for ack from
        // mac before moving on.
        if(rd_retransmit == 1'b1) begin
          rd4b_nxt_sm <= RETRANSMIT_s;
        end
        else if(tx_ack_i == 1'b1) begin
          rd4b_nxt_sm <= FRAME_s;
        end
        else begin
          rd4b_nxt_sm <= WAIT_ACK_s;
        end
      end

      FRAME_s : begin
        // if a collision only request, then must drop the rest of the
        // current frame, move to drop state
        if(rd_drop_frame == 1'b1) begin
          rd4b_nxt_sm <= DROP_s;
        end
        else if(rd_retransmit == 1'b1) begin
          rd4b_nxt_sm <= RETRANSMIT_s;
        // continue transmitting frame until the end of the frame is
        // detected, then wait for a new frame to be sent.
        end
        else if(rd_eof_follow == 1'b1) begin
          rd4b_nxt_sm <= IDLE_s;
        end
        else begin
          rd4b_nxt_sm <= FRAME_s;
        end
      end

      DROP_s : begin
        // wait until rest of frame has been cleared.
        if(rd_eof_follow == 1'b1) begin
          rd4b_nxt_sm <= IDLE_s;
        end
        else begin
          rd4b_nxt_sm <= DROP_s;
        end
      end

      RETRANSMIT_s : begin
        // reload the data pipe from the start of the frame
        rd4b_nxt_sm <= QUEUE1_s;
      end

      default : begin
        rd4b_nxt_sm <= IDLE_s;
      end
    endcase
  end
end // gen_hd_sm  // half duplex state machine
endgenerate

//----------------------------------------------------------------------------
// decode output signals
// decode output data
always@(posedge rd_clk_i) begin
  if(rd_enable_i == 1'b1) begin
    if(rd4b_nxt_sm == FRAME_s)
      txdata8b_o <= rddata8b_pipe;
    else begin
      case(rd4b_sm)
        QUEUE_ACK_s :
          txdata8b_o <= rddata8b_pipe;
        WAIT_ACK_s :
          txdata8b_o <= txdata8b_o;
        FRAME_s :
          txdata8b_o <= rddata8b_pipe;
        default :
          txdata8b_o <= 8'b0;
      endcase
    end
  end
end

// decode output data valid
always@(posedge rd_clk_i) begin
  if(rd_enable_i == 1'b1) begin
    if(rd4b_nxt_sm == FRAME_s)
      txdata_valid_o <= ~(tx_collision_i && ~(tx_retransmit_i));
    else begin
      case(rd4b_sm)
        QUEUE_ACK_s :
          txdata_valid_o <= 1'b1;
        WAIT_ACK_s :
          txdata_valid_o <= 1'b1;
        FRAME_s :
          txdata_valid_o <= ~(rd4b_nxt_sm == DROP_s);
        default :
          txdata_valid_o <= 1'b0;
      endcase
     end
  end
end

//----------------------------------------------------------------------------
// decode full duplex only control signals
generate if(FULL_DUPLEX_ONLY == 1) begin : gen_fd_decode

  assign rd_en_w = (rd4b_sm == IDLE_s) ? 1'b0 :
                 (rd4b_nxt_sm == FRAME_s) ? 1'b1 :
                 (rd4b_sm == WAIT_ACK_s) ? 1'b0 : 1'b1;

  assign rd_addrInc_w = rd_en_w;

  assign rd_addrReload_w = (rd4b_sm == FRAME_s && rd4b_nxt_sm == IDLE_s)
                          ? 1'b1 : 1'b0;

  // Transmit frame pulse is only 1 clock enabled pulse long.
  // Transmit frame pulse must never be more frequent than 64 clocks to allow
  // toggle to cross clock domain
  assign rd_transmitFrame_w = (rd4b_sm == WAIT_ACK_s && rd4b_nxt_sm == FRAME_s)
                             ? 1'b1 : 1'b0;

  // unused for full duplex only
  assign rd_startAddr_reload_w = 1'b0;
  assign rd_startAddr_load_w   = 1'b0;
  assign rd_retransmitFrame_w  = 1'b0;

end // gen_fd_decode // full duplex control signals
endgenerate

//----------------------------------------------------------------------------
// decode half duplex control signals
generate if(FULL_DUPLEX_ONLY != 1) begin : gen_hd_decode

  assign rd_en_w = (rd4b_sm == IDLE_s) ? 1'b0 :
                 (rd4b_nxt_sm == DROP_s && rd_eof == 1'b1) ? 1'b0 :
                 (rd4b_nxt_sm == FRAME_s) ? 1'b1 :
                 (rd4b_sm == RETRANSMIT_s) ? 1'b0 :
                 (rd4b_sm == WAIT_ACK_s) ? 1'b0 : 1'b1;

  assign rd_addrInc_w = rd_en_w;

  assign rd_addrReload_w = (rd4b_sm == FRAME_s && rd4b_nxt_sm == IDLE_s)
                          ? 1'b1 :
                          (rd4b_sm == DROP_s && rd4b_nxt_sm == IDLE_s)
                          ? 1'b1 : 1'b0;

  assign rd_startAddr_reload_w = (rd4b_sm == RETRANSMIT_s) ? 1'b1 : 1'b0;

  assign rd_startAddr_load_w = (rd4b_sm== WAIT_ACK_s && rd4b_nxt_sm == FRAME_s)
                              ? 1'b1 :
                              (rd_colWindow_expire == 1'b1) ? 1'b1 : 1'b0;

  // Transmit frame pulse must never be more frequent than 64 clocks to allow
  // toggle to cross clock domain
  assign rd_transmitFrame_w = (rd4b_sm == WAIT_ACK_s && rd4b_nxt_sm == FRAME_s)
                             ? 1'b1 : 1'b0;

  // Retransmit frame pulse must never be more frequent than 16 clocks to allow
  // toggle to cross clock domain
  assign rd_retransmitFrame_w = (rd4b_sm == RETRANSMIT_s) ? 1'b1 : 1'b0;

end // gen_hd_decode                           // half duplex control signals
endgenerate

//----------------------------------------------------------------------------
// Frame Count
// We need to maintain a count of frames in the fifo, so that we know when a
// frame is available for transmission.  The counter must be held on the
// write clock domain as this is the faster clock.
//----------------------------------------------------------------------------

// A frame has been written to the fifo
assign wr_storeFrame_w = (wr2b_sm == EOF_s && wr2b_nxt_sm != EOF_s)
                        ? 1'b1 : 1'b0;

// generate a toggle to indicate when a frame has been transmitted from the
// fifo
always@(posedge rd_clk_i) begin  // process
  if(rd_enable_i == 1'b1) begin
    if(rd_transmitFrame_w == 1'b1) begin
      rdTran_frameTog <= !rdTran_frameTog;
    end
  end
end

// move the read transmit frame signal onto the write clock domain
sync_block resync_rd_tran_frame_tog (
.clk_i(wr_clk_i),
.data_i(rdTran_frameTog),
.data_o(wrTran_frameSync_w)
);

// edge detect of the resynchronized transmit frame signal
always@(posedge wr_clk_i) begin
  wrTran_frameDelay <= wrTran_frameSync_w;
end

always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr_transmitFrame <= 1'b0;
  end
  else begin
    // edge detector
    if((wrTran_frameDelay ^ wrTran_frameSync_w) == 1'b1) begin
      wr_transmitFrame    <= 1'b1;
    end
    else begin
      wr_transmitFrame    <= 1'b0;
    end
  end
end

//----------------------------------------------------------------------------
generate if(FULL_DUPLEX_ONLY == 1) begin : gen_fd_count
  // count the number of frames in the fifo.  the counter is incremented when a
  // frame is stored and decremented when a frame is transmitted.  Need to keep
  // the counter on the write clock as this is the fastest clock.
  always@(posedge wr_clk_i) begin
    if(wr_sreset_i == 1'b1) begin
      wr9b_frames <= 9'b0;
    end
    else begin
      if((wr_storeFrame_w & !wr_transmitFrame) == 1'b1) begin
        wr9b_frames <= wr9b_frames + 1;
      end
      else if((!wr_storeFrame_w & wr_transmitFrame) == 1'b1) begin
        wr9b_frames <= wr9b_frames - 1;
      end
    end
  end
end // gen_fd_count
endgenerate

//----------------------------------------------------------------------------
generate if(FULL_DUPLEX_ONLY != 1) begin : gen_hd_count
  // generate a toggle to indicate when a frame has been transmitted from the
  // fifo
  always@(posedge rd_clk_i) begin  // process
    if(rd_enable_i == 1'b1) begin
      if(rd_retransmitFrame_w == 1'b1) begin
        rdreTran_frameTog <= !rdreTran_frameTog;
      end
    end
  end

  // move the read transmit frame signal onto the write clock domain
  sync_block resync_rd_tran_frame_tog (
    .clk_i(wr_clk_i),
    .data_i(rdreTran_frameTog),
    .data_o(wrRetran_frameSync_w)
  );

  // edge detect of the resynchronized read transmit frame signal
  always@(posedge wr_clk_i) begin
    wrRetran_frameDelay <= wrRetran_frameSync_w;
  end
  
  always@(posedge wr_clk_i) begin
    if(wr_sreset_i == 1'b1) begin
      wr_retransmitFrame <= 1'b0;
    end
    else begin
      // edge detector
      if((wrRetran_frameDelay ^ wrRetran_frameSync_w) == 1'b1) begin
        wr_retransmitFrame <= 1'b1;
      end
      else begin
        wr_retransmitFrame <= 1'b0;
      end
    end
  end

  // count the number of frames in the fifo.  the counter is incremented when a
  // frame is stored or retransmitted and decremented when a frame is tx'd.
  // Need to keep the counter on the write clock as this is the fastest clock.
  // Assumes transmit and retransmit cannot happen at same time
  always@(posedge wr_clk_i) begin
    if(wr_sreset_i == 1'b1) begin
      wr9b_frames <= 9'b0;
    end
    else begin
      if((wr_storeFrame_w & wr_retransmitFrame) == 1'b1) begin
        wr9b_frames <= wr9b_frames + 2;
      end
      else if(((wr_storeFrame_w | wr_retransmitFrame)
                 & !wr_transmitFrame) == 1'b1) begin
           wr9b_frames <= wr9b_frames + 1;
      end
      else if(wr_transmitFrame == 1'b1 & !wr_storeFrame_w) begin
         wr9b_frames <= wr9b_frames - 1;
      end
    end
  end

end // gen_hd_count
endgenerate


//----------------------------------------------------------------------------
// generate a frame in fifo signal for use in control logic
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr_frame_INfifo <= 1'b0;
  end
  else begin
    if(wr9b_frames != 9'b0) begin
      wr_frame_INfifo <= 1'b1;
    end
    else begin
      wr_frame_INfifo <= 1'b0;
    end
  end
end

// register back onto read domain for use in the read logic
sync_block resync_wr_frame_in_fifo (
.clk_i(rd_clk_i),
.data_i(wr_frame_INfifo),
.data_o(frame_INfifo_w)
);

//----------------------------------------------------------------------------
// Address counters
//----------------------------------------------------------------------------

// Address counters
// write address is incremented when write enable signal has been asserted
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr12b_addr <= 12'b0;
  end
  else if(wr_addrReload_w == 1'b1) begin
    wr12b_addr <= wr12b_startAddr;
  end
  else if(wr_addrInc_w == 1'b1) begin
    wr12b_addr <= wr12b_addr + 1;
  end
end

// store the start address incase the address must be reset
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr12b_startAddr <= 12'b0;
  end
  else if(wr_startAddr_load_w == 1'b1) begin
    wr12b_startAddr <= wr12b_addr + 1;
  end
end

//----------------------------------------------------------------------------
generate if(FULL_DUPLEX_ONLY == 1) begin : gen_fd_addr
  // read address is incremented when read enable signal has been asserted
  always@(posedge rd_clk_i) begin
    if(rd_sreset_i == 1'b1) begin
      rd12b_addr <= 12'b0;
    end
    else if(rd_enable_i == 1'b1) begin
      if(rd_addrReload_w == 1'b1) begin
        rd12b_addr <= rd12b_dec_addr;
      end
      else if(rd_addrInc_w == 1'b1) begin
        rd12b_addr <= rd12b_addr + 1;
      end
    end
  end

  // do not need to keep a start address, but the address is needed to
  // calculate fifo occupancy.
  always@(posedge rd_clk_i) begin
    if(rd_sreset_i == 1'b1) begin
      rd12b_startAddr <= 12'b0;
    end
    else if(rd_enable_i == 1'b1) begin
      rd12b_startAddr <= rd12b_addr;
    end
  end

end // gen_fd_addr // full duplex address counters
endgenerate

//----------------------------------------------------------------------------
generate if(FULL_DUPLEX_ONLY != 1) begin : gen_hd_addr
  // read address is incremented when read enable signal has been asserted
  always@(posedge rd_clk_i) begin
    if(rd_sreset_i == 1'b1) begin
      rd12b_addr <= 12'b0;
    end
    else if(rd_enable_i == 1'b1) begin
      if(rd_addrReload_w == 1'b1) begin
        rd12b_addr <= rd12b_dec_addr;
      end
      else if(rd_startAddr_reload_w == 1'b1) begin
        rd12b_addr <= rd12b_startAddr;
      end
      else if(rd_addrInc_w == 1'b1) begin
        rd12b_addr <= rd12b_addr + 1;
      end
    end
  end

  always@(posedge rd_clk_i) begin
    if(rd_sreset_i == 1'b1) begin
      rd12b_startAddr <= 12'b0;
    end
    else if(rd_enable_i == 1'b1) begin
      if(rd_startAddr_load_w == 1'b1) begin
        rd12b_startAddr <= rd12b_addr - 4;
      end
    end
  end

  // Collision window expires after MAC has been transmitting for required slot
  // time.  This is 512 clock cycles at 1G.  Also if the end of frame has fully
  // been transmitted by the mac then a collision cannot occur.
  // this collision expire signal goes high at 768 cycles from the start of the
  // frame.
  // inefficient for short frames, however should be enough to prevent fifo
  // locking up.
  always@(posedge rd_clk_i) begin
    if(rd_sreset_i == 1'b1) begin
      rd_colWindow_expire <= 1'b0;
    end
    else if(rd_enable_i == 1'b1) begin
      if(rd_transmitFrame_w == 1'b1) begin
        rd_colWindow_expire <= 1'b0;
      end
      else if(rd10b_slotTimer[9:8] == 2'b11) begin
        rd_colWindow_expire <= 1'b1;
      end
    end
  end

  assign rd_idleState_w = (rd4b_sm == IDLE_s) ? 1'b1 : 1'b0;

  always@(posedge rd_clk_i) begin
    if(rd_enable_i == 1'b1) begin
      rd_colWindow_pipe[0] <= rd_colWindow_expire & rd_idleState_w;
      if(rdTxfer_en_w == 1'b1) begin
        rd_colWindow_pipe[1] <= rd_colWindow_pipe[0];
      end
    end
  end

  always@(posedge rd_clk_i) begin
    if(rd_sreset_i == 1'b1) begin   // will not count until after first
                                   // frame is sent.
      rd10b_slotTimer <= 10'b0;
    end
    else if(rd_enable_i == 1'b1) begin
      if(rd_transmitFrame_w == 1'b1) begin  // reset counter
        rd10b_slotTimer <= 10'b0;
      end
      // do not allow counter to role over.
      // only count when frame is being transmitted.
      else if(rd10b_slotTimer != 10'b1111111111) begin
        rd10b_slotTimer <= rd10b_slotTimer + 1;
      end
    end
  end

end // gen_hd_addr  // half duplex address counters
endgenerate

always@(posedge rd_clk_i) begin
  if(rd_sreset_i == 1'b1) begin
    rd12b_dec_addr <= 12'b0;
  end
  else if(rd_enable_i == 1'b1) begin
    if(rd_addrInc_w == 1'b1) begin
      rd12b_dec_addr <= rd12b_addr - 1;
    end
  end
end

//----------------------------------------------------------------------------
always@(posedge rd_clk_i) begin
  if(rd_sreset_i == 1'b1) begin
    rd_bramU <= 1'b0;
    rd_bramU_follow <= 1'b0;
  end
  else if(rd_enable_i == 1'b1) begin
    if(rd_addrInc_w == 1'b1) begin
      rd_bramU <= rd12b_addr[11];
      rd_bramU_follow <= rd_bramU;
    end
  end
end

//----------------------------------------------------------------------------
// Data Pipelines
//----------------------------------------------------------------------------

// register input signals to fifo
// no reset to allow srl16 target
always@(posedge wr_clk_i) begin
  wrdata8b_pipe[0] <= wrdata8b_i;

  if(wr_accept_pipe[0] == 1'b1) begin
    wrdata8b_pipe[1] <= wrdata8b_pipe[0];
  end

  if(wr_accept_pipe[1] == 1'b1) begin
    wrdata8b_bram    <= wrdata8b_pipe[1];
  end
end

// no reset to allow srl16 target
always@(posedge wr_clk_i) begin
  wr_sof_pipe[0] <= !wr_sofN_i;
  if(wr_accept_pipe[0] == 1'b1) begin
    wr_sof_pipe[1] <= wr_sof_pipe[0];
  end
end

always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr_accept_pipe[0] <= 1'b0;
    wr_accept_pipe[1] <= 1'b0;
    wr_accept_bram <= 1'b0;
  end
  else begin
    wr_accept_pipe[0] <= !wr_src_rdyN_i & !wr_dstRdy_intN_w;
    wr_accept_pipe[1] <= wr_accept_pipe[0];
    wr_accept_bram <= wr_accept_pipe[1];
  end
end

always@(posedge wr_clk_i) begin
  wr_eof_pipe[0] <= !wr_eofN_i;

  if(wr_accept_pipe[0] == 1'b1) begin
    wr_eof_pipe[1] <= wr_eof_pipe[0];
  end

  if(wr_accept_pipe[1] == 1'b1) begin
    wr_eof_bram[0] <= wr_eof_pipe[1];
  end
end

// register data outputs from bram
// no reset to allow srl16 target
always@(posedge rd_clk_i) begin
  if(rd_enable_i == 1'b1) begin
    if(rd_en_w == 1'b1) begin
      rddata8b_pipeU <= rddata8b_bramU_w;
      rddata8b_pipeL <= rddata8b_bramL_w;

      if(rd_bramU_follow == 1'b1) begin
        rddata8b_pipe <= rddata8b_pipeU;
      end
      else begin
        rddata8b_pipe <= rddata8b_pipeL;
      end
    end
  end
end

// register data outputs from bram
// no reset to allow srl16 target
always@(posedge rd_clk_i) begin
  if(rd_enable_i == 1'b1) begin
    if(rd_en_w == 1'b1) begin
      if(rd_bramU == 1'b1) begin
        rd_eof_pipe <= rd_eof_bramU_w[0];
      end
      else begin
        rd_eof_pipe <= rd_eof_bramL_w[0];
      end
      rd_eof <= rd_eof_pipe;
      rd_eof_follow <= rd_eof | rd_eof_pipe;
    end
  end
end

//----------------------------------------------------------------------------
generate if(FULL_DUPLEX_ONLY != 1) begin : gen_hd_input
  // register the collision and retransmit signals
  always@(posedge rd_clk_i) begin
    if(rd_enable_i == 1'b1) begin
      rd_drop_frame <= tx_collision_i & !tx_retransmit_i;
    end
  end

  always@(posedge rd_clk_i) begin
    if(rd_enable_i == 1'b1) begin
      rd_retransmit <= tx_collision_i & tx_retransmit_i;
    end
  end

end   // gen_hd_input   // half duplex register input
endgenerate


//----------------------------------------------------------------------------
// Fifo full functionality
//----------------------------------------------------------------------------

// Full functionality is difference between read and write addresses.
// Cannot use gray code this time as the read address and read start addresses
// jump by more than 1

// generate an enable pulse for the read side every 16 read clocks.  This
// provides for the worst case situation where wr clk is 20Mhz and rd clk is
// 125 Mhz.
always@(posedge rd_clk_i) begin
  if(rd_sreset_i == 1'b1) begin
    rd4b_16count <= 4'b0;
  end
  else if(rd_enable_i == 1'b1) begin
    rd4b_16count <= rd4b_16count + 1;
  end
end

assign rdTxfer_en_w = (rd4b_16count == 4'b1111) ? 1'b1 : 1'b0;

// register the start address on the enable pulse
always@(posedge rd_clk_i) begin
  if(rd_sreset_i == 1'b1) begin
    rd12b_addr_txfer <= 12'b0;
  end
  else if(rd_enable_i == 1'b1) begin
    if(rdTxfer_en_w == 1'b1) begin
      rd12b_addr_txfer <= rd12b_startAddr;
    end
  end
end

// generate a toggle to indicate that the address has been loaded.
always@(posedge rd_clk_i) begin
  if(rd_enable_i == 1'b1) begin
    if(rdTxfer_en_w == 1'b1) begin
      rdTxfer_tog <= !rdTxfer_tog;
    end
  end
end

// pass the toggle to the write side
sync_block resync_rd_txfer_tog (
.clk_i(wr_clk_i),
.data_i(rdTxfer_tog),
.data_o(wrTxfer_togSync_w)
);

always@(posedge wr_clk_i) begin
  wrTxfer_togDelay <= wrTxfer_togSync_w;
end

// generate an enable pulse from the toggle, the address should have been
// steady on the wr clock input for at least one clock
assign wrTxfer_en_w = wrTxfer_togDelay ^ wrTxfer_togSync_w;

// capture the address on the write clock when the enable pulse is high.
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr12b_rd_addr <= 12'b0;
  end
  else if(wrTxfer_en_w == 1'b1) begin
    wr12b_rd_addr <= rd12b_addr_txfer;
  end
end


// Obtain the difference between write and read pointers
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr12b_addr_diff <= 12'b0;
  end
  else begin
    wr12b_addr_diff <= wr12b_rd_addr - wr12b_addr;
  end
end

// Detect when the FIFO is full
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr_fifo_full <= 1'b0;
  end
  else begin
    // The FIFO is considered to be full if the write address
    // pointer is within 1 to 3 of the read address pointer.
    if(wr12b_addr_diff[11:4] == 8'b0 && wr12b_addr_diff[3:2] != 2'b0) begin
       wr_fifo_full <= 1'b1;
    end
    else begin
      wr_fifo_full <= 1'b0;
    end
  end
end

// memory overflow_o occurs when the fifo is full and there are no frames
// available in the fifo for transmission.  If the collision window has
// expired and there are no frames in the fifo and the fifo is full, then the
// fifo is in an overflow_o state.  we must accept the rest of the incoming
// frame in overflow_o condition.

generate if(FULL_DUPLEX_ONLY == 1) begin : gen_fd_ovflow
   // in full duplex mode, the fifo memory can only overflow_o if the fifo goes
   // full but there is no frame available to be retranmsitted.
   // Therefore, prevent signal from being asserted when store_frame signal is
   // high, as frame count is being updated.
   assign wr_fifoOverflow_w = (wr_fifo_full == 1'b1 && wr_frame_INfifo == 1'b0
                                 && wr_eofState_w == 1'b0
                                 && wr_eofState == 1'b0)
                              ? 1'b1 : 1'b0;
end // gen_fd_ovflow
endgenerate

generate if(FULL_DUPLEX_ONLY != 1) begin : gen_hd_ovflow
  // register wr col window to give address counter sufficient time to update.
  // This will prevent signal from being asserted when store_frame signal is
  // high, as frame count is being updated
  assign wr_fifoOverflow_w = (wr_fifo_full == 1'b1 && wr_frame_INfifo == 1'b0
                                && wr_eofState_w == 1'b0
                                && wr_eofState == 1'b0
                                && wr_colWindow_expire == 1'b1)
                             ? 1'b1 : 1'b0;

  // register rd_col_window signal
  // this signal is long, and will remain high until overflow_o functionality
  // has finished, so save just to register the once.
  always@(posedge wr_clk_i) begin  // process
    if(wr_sreset_i == 1'b1) begin
      wr_colWindow_pipe[0] <= 1'b0;
      wr_colWindow_pipe[1] <= 1'b0;
      wr_colWindow_expire  <= 1'b0;
    end
    else begin
      if(wrTxfer_en_w == 1'b1) begin
          wr_colWindow_pipe[0] <= rd_colWindow_pipe[1];
      end
      wr_colWindow_pipe[1] <= wr_colWindow_pipe[0];
      wr_colWindow_expire <= wr_colWindow_pipe[1];
    end
  end
end // gen_hd_ovflow
endgenerate

//----------------------------------------------------------------------------
// Create FIFO Status Signals in the Write Domain
//----------------------------------------------------------------------------

// The FIFO status signal is four bits which represents the occupancy
// of the FIFO in 16'ths.  To generate this signal we therefore only
// need to compare the 4 most significant bits of the write address
// pointer with the 4 most significant bits of the read address
// pointer.

// The 4 most significant bits of the write pointer minus the 4 msb of
// the read pointer gives us our FIFO status.
always@(posedge wr_clk_i) begin
  if(wr_sreset_i == 1'b1) begin
    wr4b_fifostatus_o <= 4'b0;
  end
  else begin
    if(wr12b_addr_diff == 12'b0) begin
      wr4b_fifostatus_o <= 4'b0;
    end
    else begin
      wr4b_fifostatus_o[3] <= !wr12b_addr_diff[11];
      wr4b_fifostatus_o[2] <= !wr12b_addr_diff[10];
      wr4b_fifostatus_o[1] <= !wr12b_addr_diff[9];
      wr4b_fifostatus_o[0] <= !wr12b_addr_diff[8];
    end
  end
end

assign rd_en_bram_w = rd_en_w & rd_enable_i;

//----------------------------------------------------------------------------
// Instantiate Memory
//----------------------------------------------------------------------------
bram ramgen_txl ( 
.doa(),     //no use
.cea(1'b1),
.dia({wr_eof_bram,wrdata8b_bram}), 
.addra(wr12b_addr[10:0]), 
.clka(wr_clk_i), 
.wea(wr_enL_w), 
//.rsta(wr_sreset_i), 
.dob({rd_eof_bramL_w,rddata8b_bramL_w}), 
.dib(9'b0),   //no use
.addrb(rd12b_addr[10:0]), 
.clkb(rd_clk_i), 
.web(1'b0), 
.ceb(rd_en_bram_w)
//.rstb(rd_sreset_i)
);

bram ramgen_txu ( 
.doa(),   //no use
.cea(1'b1), 
.dia({wr_eof_bram,wrdata8b_bram}), 
.addra(wr12b_addr[10:0]), 
.clka(wr_clk_i), 
.wea(wr_enU_w), 
//.rsta(wr_sreset_i), 
.dob({rd_eof_bramU_w,rddata8b_bramU_w}), 
.dib(9'b0),   //no use 
.addrb(rd12b_addr[10:0]), 
.clkb(rd_clk_i), 
.web(1'b0), 
.ceb(rd_en_bram_w)
//.rstb(rd_sreset_i)
);

endmodule